/*
 *  Copyright (C) 2009  Peter Kist & Jan Ripke
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "Manager.h"
#include "Consumer.h"
#include "Producer.h"
#include "GfxManagerThread.h"

#include <gfx/Interface.h>
#include <gfx/ogre/engine/galaxy/GalaxyConsumer.h>
#include <gfx/ogre/gui/MenuManager.h>

#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>

#include <core/logger.h>
#include <galaxy/Galaxy.h>
#include <mediator/Interface.h>
#include <mediator/InterMediator.h>
#include <mediator/Event.h>
#include <input/MediatorTypes.h>

#include <Ogre.h>

using namespace boost::posix_time;

namespace galaxy {

    namespace gfx {

        namespace ogre3d {

            Manager::Manager(void)
            {
                m_Root = 0L;
                m_GfxManagerThread = 0L;

                m_Producer.reset(new Producer(*this));
                m_Consumer.reset(new Consumer(*this));
                m_MenuManager.reset(new gui::MenuManager(*this));
                m_GalaxyConsumer = new engine::galactic::GalaxyConsumer (*this);

                //m_GalaxyConsumer = 0L;
            }

            Manager::~Manager(void)
            {
            }

            bool Manager::initialize () {

                AppContext& appContext = AppContext::singleton ();

                std::string basename = appContext.paths.m_AppName.stem();
                std::string plugins = (appContext.paths.config / (basename + ".plugins")).string();
                std::string ogrecfg = (appContext.paths.config / (basename + ".ogrecfg")).string();
                std::string ogrelog = (appContext.paths.logs / (basename + ".log")).string();

                try {

                    m_Root = new Ogre::Root(plugins, ogrecfg, ogrelog);

                    defineResources();
                    setupRenderSystem();
                    createRenderWindow();
                    initializeResourceGroups();
                    setupScene();
                    if (setupCEGUI() == false) return false;

                } catch (Ogre::Exception& e) {
                    LOG_ERROR (e.what());
                    return false;
                }

                /* producers */
                mediator::InterMediator::registerProducer (*m_Producer);

                /* consumers */
                // handle all events from input module
                mediator::InterMediator::registerConsumer (*m_Consumer);
                mediator::InterMediator::registerInterest (m_Consumer->consumerType (),
                    mediator::Identifier(mediator::CATEGORY_INPUT, mediator::Identifier::any,
                    mediator::Identifier::any));
                mediator::InterMediator::registerInterest (m_Consumer->consumerType (),
                    mediator::Identifier(mediator::CATEGORY_GFX, mediator::Identifier::any,
                    mediator::Identifier::any));

                // handle all GalaxyEngine generated events
                mediator::InterMediator::registerConsumer (*m_GalaxyConsumer);
                mediator::InterMediator::registerInterest (m_GalaxyConsumer->consumerType(),
                    mediator::Identifier(mediator::CATEGORY_GFX,
                    MODULE_TYPE_GFX_ENTITY_COMMAND, mediator::Identifier::any));

                return true;
            }

            bool Manager::postInitialize () {
                return true;
            }

            boost::thread * Manager::startManagerThread () {
                ManagerPtr myself = shared_from_this();
                m_GfxManagerThread = new GfxManagerThread (myself);
                boost::thread * thrd = new boost::thread(*m_GfxManagerThread);

                // give thread a chance to start
                while (m_GfxManagerThread->m_State != core::Thread::THREAD_STATE_RUNNING) {
                    boost::this_thread::yield();
                }

                return thrd;
            }

            void Manager::terminateThread () {
                if (m_GfxManagerThread) {
                    m_GfxManagerThread->m_State = core::Thread::THREAD_STATE_STOP_REQUESTED;
                }
            }

            bool Manager::preDestroy () {
                return true;
            }

            bool Manager::destroy () {
                return true;
            }



            void Manager::defineResources()
            {
                Ogre::String secName, typeName, archName;
                Ogre::ConfigFile cf;

                try {
                    AppContext& appContext = AppContext::singleton ();
                    std::string resourceFile = appContext.paths.m_AppName.stem() + ".resources";

                    // filename resource file: $(config)/$(m_AppName).resources
                    cf.load ( (appContext.paths.config / resourceFile).string() ); 

                    Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
                    while (seci.hasMoreElements())
                    {
                        secName = seci.peekNextKey();
                        Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
                        Ogre::ConfigFile::SettingsMultiMap::iterator i;

                        for (i = settings->begin(); i != settings->end(); ++i)
                        {
                            typeName = i->first;
                            archName = i->second;
                            Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName,
                                typeName, secName);
                        }
                    }

                } catch (Ogre::Exception& e) {
                    std::cerr << e.what () << std::endl;
                    throw std::exception(e.what());
                }
            }

            void Manager::setupRenderSystem()
            {
                if (!m_Root->restoreConfig() && !m_Root->showConfigDialog()) {
                    throw Ogre::Exception(52, "User canceled the config dialog!",
                        "gfx::Manager::setupRenderSystem()");
                }
            }

            void Manager::createRenderWindow()
            {
                // initialize render window
                m_Root->initialise(true, "MP-Galaxy");
            }

            void Manager::initializeResourceGroups()
            {
                Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);
                Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
            }

            void Manager::setupScene()
            {
                Ogre::SceneManager *sceneManager = m_Root->createSceneManager(Ogre::ST_GENERIC,
                    "Default SceneManager");
                Ogre::Camera *camera = sceneManager->createCamera("Camera");
                Ogre::Viewport *vp = m_Root->getAutoCreatedWindow()->addViewport(camera);

                // FIXME: remove this code to setup the camera and tie it to the player entity
                camera->setPosition(0, 0, 200);
                camera->lookAt(Ogre::Vector3(0.0,0.0,0.0));
                camera->setNearClipDistance(0.5);
                camera->setFarClipDistance(10000);
            }

            void Manager::setupInputSystem()
            {
            }

            bool Manager::setupCEGUI()
            {
                if (m_MenuManager->initialize () == false) {
                    return false;
                }
                return true;
            }

            void Manager::createFrameListener()
            {
            }

            void Manager::terminate () {
            }

        } // namespace ogre3d

    } // namespace gfx

} // namespace galaxy
